
#include "glue.h"
#include "GlueTypes.h"
#include "PCM_sourceGlue.h"
#include "PCM_sinkGlue.h"
#include "MidiIOGlue.h"

#include <assert.h>

// if we pre-define this then the varbles will be local

#define REAPER_PLUGIN_DECLARE_APIFUNCS

#include "reaper_plugin/reaper_plugin_functions.h"


// NOTE:  you need the library lua5.1.lib in
// http://luaforge.net/frs/download.php/3706/lua5_1_4_Win32_vc8_lib.zip
// to compile ReaLua.  Just put it in the same directory as
// the .sln file.

bool checkFunc(void *fp, char *fname)
{
	assert(fname != NULL);

	if( !fp )
	{
		printf("\nReaLua:  %s is not available - GetFunc() returned null.\n", fname);
		return false;
	}

	return true;
}

bool badFunc(void *fp, char *fname)
{
	return !( checkFunc(fp, fname) );
}

// takes void returns void

/*
	assertFunctionPointer( TrackFX_GetFXName );
*/

static int fvoid2void(void (f(void)) , char *fname  )
{
	assertFunc(f, fname);

	f();

	return 0;
}

#define mkvoid2void(NAME) \
	static int f##NAME(lua_State *L) { return fvoid2void( NAME , #NAME ); }

mkvoid2void(UpdateTimeline);
mkvoid2void(ClearAllRecArmed);
mkvoid2void(ClearPeakCache);
mkvoid2void(TrackList_UpdateAllExternalSurfaces);
mkvoid2void(CSurf_SetTrackListChange);

mkvoid2void(CSurf_OnPlay);
mkvoid2void(CSurf_OnStop);
mkvoid2void(CSurf_OnRecord);
mkvoid2void(CSurf_GoStart);
mkvoid2void(CSurf_GoEnd);
mkvoid2void(CSurf_ResetAllCachedVolPanStates);

// takes a boolean and returns void

static int fbool2void(lua_State *L , void f(bool) , char *fname)
{
	assertFunc(f, fname);

	bool argu = luaL_checkboolean(L, 1);
	f(argu);
	return 0;
}

#define mkbool2void(NAME) \
	static int f##NAME(lua_State *L) { return fbool2void( L , NAME , #NAME ); }

mkbool2void(MuteAllTracks);
mkbool2void(TrackList_AdjustWindows);
mkbool2void(CSurf_FlushUndo);

// takes int returns void

static int fint2void( lua_State *L , void ( f(int) ) , char *fname)
{
	assertFunc(f, fname);

	int argu = luaL_checkinteger(L, 1);
	f(argu);
	return 0;
}

#define mkint2void(NAME) \
	static int f##NAME(lua_State *L) { return fint2void( L , NAME , #NAME); }

mkint2void(SoloAllTracks);
mkint2void(BypassFxAllTracks);
mkint2void(Main_UpdateLoopInfo);
mkint2void(CSurf_OnFwd);
mkint2void(CSurf_OnRew);

// int (*GetSetRepeat)(int val); // -1 == query, 0=clear, 1=set, >1=toggle
// takes int returns int

static int fGetSetRepeat(lua_State *L)
{
	assertFunctionPointer( GetSetRepeat );

	int arg1 = luaL_checkinteger(L, 1);
	int anser = GetSetRepeat(arg1);
	lua_pushinteger(L, anser);

	return 1;	// success
}

// takes (const char *, char *, int) returns void
// takes string returns string

static int frfn(lua_State *L, void f(const char *, char *, int) , char *fname )
{
	assertFunc(f, fname);

	char buf[BUFSIZE];
	const char *arg1 = luaL_checkstring(L, 1);
	f(arg1, buf, BUFSIZE);
	lua_pushstring(L, buf);

	return 1;
}

#define mkfrfn(NAME) \
	static int f##NAME(lua_State *L) { return frfn( L , NAME , #NAME ); }

mkfrfn(resolve_fn);
mkfrfn(relative_fn);
mkfrfn(GetPeakFileName);

// takes void and returns int

static int fvoid2int( lua_State *L, int f(void) , char *fname )
{
	assertFunc(f, fname);

	int anser = f();
	lua_pushnumber(L, anser);
	return 1;
}

#define mkvoid2int(NAME) \
	static int f##NAME(lua_State *L) { return fvoid2int( L , NAME , #NAME ); }

mkvoid2int(Audio_IsRunning);
mkvoid2int(Audio_IsPreBuffer);
mkvoid2int(IsInRealTimeAudio);
mkvoid2int(GetMaxMidiInputs);
mkvoid2int(GetMaxMidiOutputs);
mkvoid2int(GetNumTracks);
mkvoid2int(GetMasterMuteSoloFlags);
mkvoid2int(GetPlayState);
mkvoid2int(Undo_DoRedo);
mkvoid2int(Undo_DoUndo);
mkvoid2int(GetNumMIDIInputs);
mkvoid2int(GetNumMIDIOutputs);

// takes void returns double

static int fvoid2double(lua_State *L, double f(void) , char *fname )
{
	assertFunc(f, fname);

	double anser = f();
	lua_pushnumber(L, anser);
	return 1;
}

#define mkvoid2double(NAME) \
	static int f##NAME(lua_State *L) { return fvoid2double( L , NAME , #NAME ); }

mkvoid2double(GetPlayPosition);
mkvoid2double(GetPlayPosition2);
mkvoid2double(GetCursorPosition);
mkvoid2double(GetHZoomLevel);

//		void (*CSurf_ScrubAmt)(double amt);
// takes double returns void

static int fCSurf_ScrubAmt(lua_State *L)
{
	assertFunctionPointer( CSurf_ScrubAmt );

	double arg1 = luaL_checknumber(L, 1);

	CSurf_ScrubAmt(arg1);

	return 0;
}

// takes double returns double

static int fdouble2double(lua_State *L, double f(double) , char *fname )
{
	assertFunc(f, fname);

	double argu = luaL_checknumber(L, 1);
	double anser = f(argu);
	lua_pushnumber(L, anser);

	return 1;
}

#define mkdouble2double(NAME) \
	static int f##NAME(lua_State *L) { return fdouble2double( L , NAME , #NAME); }

mkdouble2double(DB2SLIDER);
mkdouble2double(SLIDER2DB);
mkdouble2double(Master_GetPlayRateAtTime);

// void (*mkvolpanstr)(char *str, double vol, double pan);
// takes (double,double) returns string

static int fmkvolpanstr(lua_State *L)
{
	assertFunctionPointer( mkvolpanstr );

	char buf[BUFSIZE];
	double arg1 = luaL_checknumber(L, 1);
	double arg2 = luaL_checknumber(L, 2);
	mkvolpanstr(buf, arg1, arg2);
	lua_pushstring(L, buf);

	return 1;
}

// takes (char *str, double xy) returns void
// takes double returns string

static int fdouble2string(lua_State *L, void f(char *, double) , char *fname)
{
	assertFunc(f, fname);

	char buf[BUFSIZE];
	double arg1 = luaL_checknumber(L, 1);
	f(buf, arg1);
	lua_pushstring(L, buf);

	return 1;
}

#define mkdouble2string(NAME) \
	static int f##NAME(lua_State *L) { return fdouble2string( L , NAME , #NAME ); }

mkdouble2string(mkvolstr);
mkdouble2string(mkpanstr);

// takes void returns string

static int fvoid2string(lua_State *L, const char *f(void) , char *fname )
{
	assertFunc(f, fname);

	const char *anser = f();
	lua_pushstring(L, anser);
	return 1;
}

#define mkvoid2string(NAME) \
	static int f##NAME(lua_State *L) { return fvoid2string( L , NAME , #NAME ); }

mkvoid2string(get_ini_file);
mkvoid2string(GetExePath);

mkvoid2string(Undo_CanUndo); // what happens when API func returns NULL?
mkvoid2string(Undo_CanRedo); // what happens when API func returns NULL?

// takes int returns string

// *** we actually have many "takes int returns string"
// which differ only in how the string is returned
// think on this.

static int fint2string(lua_State *L, const char *f(int) , char *fname)
{
	assertFunc(f, fname);

	int argu = luaL_checkinteger(L, 1);

	const char *answer = f(argu);
	if(answer)
	{
		lua_pushstring(L, answer);
		return 1;	// success
	}

	return 0;
}

#define mkint2string(NAME) \
	static int f##NAME(lua_State *L) { return fint2string( L , NAME , #NAME ); }

mkint2string(Resample_EnumModes);
mkint2string(GetOutputChannelName);
mkint2string(GetInputChannelName);

// takes (int,int) returns int

static int fGetColorTheme(lua_State *L)
{
	assertFunctionPointer( GetColorTheme );

	int arg1 = luaL_checkinteger(L, 1);
	int arg2 = luaL_checkinteger(L, 2);

	int anser = GetColorTheme(arg1, arg2);

	lua_pushnumber(L, anser);
	return 1;	// success
}

// takes void returns HWND in a lightuserdata (henceforth LUD)

static int fGetMainHwnd(lua_State *L)
{
	assertFunctionPointer( GetMainHwnd );

	HWND hw = GetMainHwnd();
	pushLud(L, hw);
	return 1;
}

// HMENU (*GetContextMenus)(); 
// takes void returns LUD(HMENU)
#if 0
static int fGetContextMenus(lua_State *L)
{
	assertFunctionPointer( GetContextMenus );

	HMENU hm = GetContextMenus();
	pushLud(L, hm);
	return 1;
}
#endif

// takes (const char *, int *) returns void *
// takes string returns (pointer in LUD, int sizeofit)

static int fget_config_var(lua_State *L)
{
	assertFunctionPointer( get_config_var );

	const char *arg1 = luaL_checkstring(L, 1);
	int arg2 = luaL_checkinteger(L, 2);
	void *answer = get_config_var(arg1, &arg2);
	pushLud(L, answer );
	lua_pushnumber(L, arg2);

	return 2;
}

// void (*GetProjectTimeSignature)(double *bpm, double *bpi)
// takes void returns (double,double)

static int fGetProjectTimeSignature(lua_State *L)
{
	assertFunctionPointer( GetProjectTimeSignature );

	double bpm, bpi;

	GetProjectTimeSignature(&bpm, &bpi);

	lua_pushnumber(L, bpm);
	lua_pushnumber(L, bpi);
	return 2;
}

// int (*InsertMedia)(char *file, int mode);  now it is
// takes (string name, int mode) returns (int success)

static int fInsertMedia(lua_State *L)
{
	assertFunctionPointer( InsertMedia );

	char *arg1 = (char *) luaL_checkstring(L, 1);
	int arg2 = luaL_checkinteger(L, 2);
	int anser = InsertMedia(arg1, arg2);
	lua_pushnumber(L, anser);
	return 1;
}

// void (*GetProjectPath)(char *buf, int bufsz);
// takes void returns string

static int fGetProjectPath(lua_State *L)
{
	assertFunctionPointer( GetProjectPath );

	char buf[BUFSIZE];

	GetProjectPath(buf, BUFSIZE);

	lua_pushstring(L, buf);

	return 1;
}

// bool (*IsMediaExtension)(const char *ext, bool wantOthers);
// takes (string, bool) returns bool

static int fIsMediaExtension(lua_State *L)
{
	assertFunctionPointer( IsMediaExtension );

	char *arg1 = (char *) luaL_checkstring(L, 1);
	bool arg2 = luaL_checkboolean(L, 2);
	bool anser = IsMediaExtension(arg1, arg2);
	lua_pushboolean(L, anser);
	return 1;
}

// const char *(*GetTrackInfo)(int track, int *flags);  
// takes (int track OR UD(MediaTrack *)) returns (string name, int flags)

static int fGetTrackInfo(lua_State *L)
{
	int idx;
	int flags;
	const char *name = NULL;

	assertFunctionPointer( GetTrackInfo );

	if(lua_isnumber(L, 1))
	{
		idx = lua_tointeger(L, 1);
		name = GetTrackInfo(idx, &flags);
	}
	else if(lua_isuserdata(L, 1))
	{
		MediaTrack *mtp = toMediaTrack(L, 1);
		name = GetTrackInfo( (int)mtp, &flags);
	}

	if(name)
	{
		lua_pushstring(L, name);
		lua_pushnumber(L, flags);

		return 2;						// success
	}

	return 0;		// no name
}

//		double (*parsepanstr)(char *str);	
// takes string returns double

static int fparsepanstr(lua_State *L)
{
	assertFunctionPointer( parsepanstr );

	const char *str = luaL_checkstring(L, 1);
	if(str)
	{
		double anser = parsepanstr( (char *) str);
		lua_pushnumber(L, anser);
		return 1;
	}

	return 0;		// erred out
}

//	void (*SetAutomationMode)(int mode, bool onlySel);
//  takes (int, bool) returns void

static int fintbool2void(lua_State *L, void f(int,bool) , char *fname )
{
	assertFunc(f, fname);

	int arg1 = luaL_checkinteger(L, 1);
	bool arg2 = luaL_checkboolean(L, 2);
	f(arg1, arg2);

	return 0;
}

#define mkintbool2void(NAME) \
	static int f##NAME(lua_State *L) { return fintbool2void( L , NAME , #NAME ); }

mkintbool2void(SetAutomationMode);
mkintbool2void(CSurf_OnArrow);

//		void (*MoveEditCursor)(double adjamt, bool dosel);
// takes (double, bool) returns void

static int fMoveEditCursor(lua_State *L)
{
	assertFunctionPointer( MoveEditCursor );

	double adjamt = luaL_checknumber(L, 1);
	bool dosel = luaL_checkboolean(L, 2);
	MoveEditCursor(adjamt, dosel);

	return 0;
}

// REAPER_PLUGIN_DECLARE_APIFUNCS void (*SetEditCurPos)(double time, bool moveview, bool seekplay);

static int fSetEditCurPos(lua_State *L)
{
	assertFunctionPointer( SetEditCurPos );

	double time = luaL_checknumber(L, 1);
	bool moveview = luaL_checkboolean(L, 2);
	bool seekplay = luaL_checkboolean(L, 3);
	SetEditCurPos(time, moveview, seekplay);

	return 0;
}

//		void (*adjustZoom)(double amt, int forceset, bool doupd, int centermode); // forceset=0, doupd=true, centermode=-1 for default
// takes (double, int, bool, int) returns void

static int fadjustZoom(lua_State *L)
{
	assertFunctionPointer( adjustZoom );

	double amt = luaL_checknumber(L, 1);
	int forceset = luaL_checkinteger(L, 2);
	bool doupd = luaL_checkboolean(L, 3);
	int centermode = luaL_checkinteger(L, 4);

	adjustZoom(amt, forceset, doupd, centermode);

	return 0;
}

// void (*genGuid)(GUID *g);
// takes void returns UD(GUID)

static int fgenGuid(lua_State *L)
{
	assertFunctionPointer( genGuid );

	GUID *gp = (GUID *) lua_newuserdata(L, sizeof(GUID));
	genGuid(gp);
	return 1;
}

// void (*guidToString)(GUID *g, char *dest); // dest should be at least 64 chars long to be safe
// takes UD(GUID) returns string

static int fguidToString(lua_State *L)
{
	assertFunctionPointer( guidToString );

	char buf[BUFSIZE];
	luaL_checktype(L, 1, LUA_TUSERDATA);
	GUID * gp = (GUID *) lua_touserdata(L, 1);
	guidToString(gp, buf);
	lua_pushstring(L, buf);
	return 1;
}

// void (*stringToGuid)(const char *str, GUID *g);
// takes string returns UD(GUID)

static int fstringToGuid(lua_State *L)
{
	assertFunctionPointer( stringToGuid );

	const char *str = luaL_checkstring(L,1);
	GUID *gp = (GUID *) lua_newuserdata(L, sizeof(GUID));
	stringToGuid(str, gp);
	return 1;	// lua_newuserdata() pushes	// return success
}

// void (*DeleteTrack)(MediaTrack *tr);
// takes UD(MediaTrack *) returns void

static int fMediaTrack2void(lua_State *L, void f(MediaTrack *) , char *fname )
{
	assertFunc(f, fname);

	MediaTrack *mtp = toMediaTrack(L, 1);
	f(mtp);

	return 0;
}

#define mkMediaTrack2void(NAME) \
	static int f##NAME(lua_State *L) { return fMediaTrack2void( L , NAME , #NAME ); }

mkMediaTrack2void(DeleteTrack);
mkMediaTrack2void(CSurf_OnTrackSelection);

//  void (*InsertTrackAtIndex)(int idx, bool wantDefaults); // inserts a track at "idx", of course this will be clamped to 0..GetNumTracks(). wantDefaults=TRUE for default envelopes/FX, otherwise no enabled fx/env
// takes (int,bool) returns void

static int fInsertTrackAtIndex(lua_State *L)
{
	assertFunctionPointer( InsertTrackAtIndex );

	int idx = luaL_checkinteger(L, 1);
	bool wantDefaults = luaL_checkboolean(L, 2);

	InsertTrackAtIndex(idx, wantDefaults);

	return 0;
}

// GUID *(*GetTrackGUID)(MediaTrack *tr);
// takes UD(MediaTrack *) returns UD(GUID)

static int fGetTrackGUID(lua_State *L)
{
	assertFunctionPointer( GetTrackGUID );

	MediaTrack *mtp = toMediaTrack(L, 1);

	GUID * gp = GetTrackGUID(mtp);

	GUID * gud = (GUID *) lua_newuserdata(L, sizeof(GUID));
	*gud = *gp;

	return 1;	// newuserdata pushed
}

//	MediaTrack *(*CSurf_TrackFromID)(int idx, bool mcpView);
// takes (int, bool) returns UD(MediaTrack *)

static int fCSurf_TrackFromID(lua_State *L)
{
	assertFunctionPointer( CSurf_TrackFromID );

	int idx = luaL_checkinteger(L, 1);
	bool mcpView = luaL_checkboolean(L, 2);
	MediaTrack *mtp = CSurf_TrackFromID(idx, mcpView);
	pushMediaTrack(L, mtp);

	return 1;
}

//		int (*CSurf_TrackToID)(MediaTrack *track, bool mcpView); 
// takes ( UD(MediaTrack *), bool ) returns int

static int fCSurf_TrackToID(lua_State *L)
{
	assertFunctionPointer( CSurf_TrackToID );

	bool mcpView = luaL_checkboolean(L, 2);
	MediaTrack *mtp = toMediaTrack(L, 1);

	int anser = CSurf_TrackToID(mtp, mcpView);

	lua_pushnumber(L, anser);

	return 1;
}

//	void (*SetTrackSelected)(MediaTrack *tr, bool sel);
// takes ( UD(MediaTrack *), bool ) returns void

static int fSetTrackSelected(lua_State *L)
{
	assertFunctionPointer( SetTrackSelected );

	MediaTrack *mtp = toMediaTrack(L, 1);
	bool sel = luaL_checkboolean(L, 2);
	SetTrackSelected(mtp, sel);

	return 0;
}

//		int (*CSurf_NumTracks)(bool mcpView);
// takes bool returns int

static int fCSurf_NumTracks(lua_State *L)
{
	assertFunctionPointer( CSurf_NumTracks );

	bool mcpView = luaL_checkboolean(L, 1);
	int answer = CSurf_NumTracks(mcpView);
	lua_pushnumber(L, answer);

	return 1;
}

// process a MIDI event/list through the standard keyboard assignable key system. For the most part dev_index should be -1
// REAPER_PLUGIN_DECLARE_APIFUNCS void (*kbd_OnMidiEvent)(MIDI_event_t *evt, int dev_index);
// REAPER_PLUGIN_DECLARE_APIFUNCS void (*kbd_OnMidiList)(MIDI_eventlist *list, int dev_index);

static int fkbd_OnMidiEvent(lua_State *L)
{
	assertFunctionPointer( kbd_OnMidiEvent );

	MIDI_event_t *evt = (MIDI_event_t *) toReaperObject(L, 1);//toMIDI_event_t(L, 1);
	int devindex = luaL_checkinteger(L, 2);

	kbd_OnMidiEvent(evt, devindex);
	return 0;
}

static int fkbd_OnMidiList(lua_State *L)
{
	assertFunctionPointer( kbd_OnMidiList );

// only remove this comment after you step into the next line
// and make sure it is returning the MIDI_eventlist pointer:
	MIDI_eventlist *list = (MIDI_eventlist *) toReaperObject(L, 1);
	int devindex = luaL_checkinteger(L, 2);

	kbd_OnMidiList(list, devindex);
	return 0;
}

// REAPER_PLUGIN_DECLARE_APIFUNCS double (*CSurf_OnVolumeChange)(MediaTrack *trackid, double volume, bool relative);
// REAPER_PLUGIN_DECLARE_APIFUNCS double (*CSurf_OnPanChange)(MediaTrack *trackid, double pan, bool relative);

static int fMediaTrackDoubleBool2Double(lua_State *L, double f(MediaTrack *, double, bool), char *fname )
{
	assertFunc(f, fname);

	MediaTrack *trackid = toMediaTrack(L, 1);
	double volpan =  luaL_checknumber(L, 2);
	bool relative = luaL_checkboolean(L, 3);

	double answer = f(trackid, volpan, relative);

	lua_pushnumber(L, answer);

	return 1;
}

#define mkMediaTrackDoubleBool2Double(NAME) \
	static int f##NAME(lua_State *L) { return fMediaTrackDoubleBool2Double( L , NAME , #NAME ); }

mkMediaTrackDoubleBool2Double(CSurf_OnVolumeChange);
mkMediaTrackDoubleBool2Double(CSurf_OnPanChange);

// REAPER_PLUGIN_DECLARE_APIFUNCS double (*CSurf_OnVolumeChangeEx)(MediaTrack *trackid, double volume, bool relative, bool allowGang);
// REAPER_PLUGIN_DECLARE_APIFUNCS double (*CSurf_OnPanChangeEx)(MediaTrack *trackid, double pan, bool relative, bool allowGang);

static int fMediaTrackDoubleBoolBool2Double(lua_State *L, double f(MediaTrack *, double, bool, bool), char *fname )
{
	assertFunc(f, fname);

	MediaTrack *trackid = toMediaTrack(L, 1);
	double volpan =  luaL_checknumber(L, 2);
	bool relative = luaL_checkboolean(L, 3);
	bool allowGang = luaL_checkboolean(L, 4);

	double answer = f(trackid, volpan, relative, allowGang);

	lua_pushnumber(L, answer);

	return 1;
}

#define mkMediaTrackDoubleBoolBool2Double(NAME) \
	static int f##NAME(lua_State *L) { return fMediaTrackDoubleBoolBool2Double( L , NAME , #NAME ); }

mkMediaTrackDoubleBoolBool2Double(CSurf_OnVolumeChangeEx);
mkMediaTrackDoubleBoolBool2Double(CSurf_OnPanChangeEx);

// REAPER_PLUGIN_DECLARE_APIFUNCS bool (*CSurf_OnMuteChange)(MediaTrack *trackid, int mute);
// REAPER_PLUGIN_DECLARE_APIFUNCS bool (*CSurf_OnSelectedChange)(MediaTrack *trackid, int selected);
// REAPER_PLUGIN_DECLARE_APIFUNCS bool (*CSurf_OnSoloChange)(MediaTrack *trackid, int solo);
// REAPER_PLUGIN_DECLARE_APIFUNCS bool (*CSurf_OnFXChange)(MediaTrack *trackid, int en);
// REAPER_PLUGIN_DECLARE_APIFUNCS bool (*CSurf_OnRecArmChange)(MediaTrack *trackid, int recarm);

static int fMediaTrackInt2Bool(lua_State *L, bool f(MediaTrack *, int), char *fname )
{
	assertFunc(f, fname);

	MediaTrack *trackid = toMediaTrack(L, 1);
	bool param =  luaL_checkboolean(L, 2);

	bool answer = f(trackid, param);

	lua_pushboolean(L, answer);

	return 1;
}

#define mkMediaTrackInt2Bool(NAME) \
	static int f##NAME(lua_State *L) { return fMediaTrackInt2Bool( L , NAME , #NAME ); }

mkMediaTrackInt2Bool(CSurf_OnMuteChange);
mkMediaTrackInt2Bool(CSurf_OnSelectedChange);
mkMediaTrackInt2Bool(CSurf_OnSoloChange);
mkMediaTrackInt2Bool(CSurf_OnFXChange);
mkMediaTrackInt2Bool(CSurf_OnRecArmChange);

//		void (*SetTrackAutomationMode)(MediaTrack *tr, int mode);
// takes ( UD(MediaTrack *), int ) returns void

static int fSetTrackAutomationMode(lua_State *L)
{
	assertFunctionPointer( SetTrackAutomationMode );

	MediaTrack *mtp = toMediaTrack(L, 1);
	int mode = luaL_checkinteger(L, 2);
	SetTrackAutomationMode(mtp, mode);

	return 0;
}

//	int (*GetTrackAutomationMode)(MediaTrack *tr);
// takes UD(MediaTrack *) returns int

static int fGetTrackAutomationMode(lua_State *L)
{
	assertFunctionPointer( GetTrackAutomationMode );

	MediaTrack *mtp = toMediaTrack(L, 1);

	int anser = GetTrackAutomationMode(mtp);

	lua_pushnumber(L, anser);
	return 1;
}

//		double (*Track_GetPeakInfo)(MediaTrack *tr, int chidx);
// takes (MediaTrack *),int returns double

static int fTrack_GetPeakInfo(lua_State *L)
{
	assertFunctionPointer( Track_GetPeakInfo );

	MediaTrack *mtp = toMediaTrack(L, 1);
	int chidx = luaL_checkinteger(L, 2);

	double anser = Track_GetPeakInfo(mtp, chidx);

	lua_pushnumber(L, anser);
	return 1;
}

//	bool (*GetTrackUIVolPan)(MediaTrack *tr, double *vol, double *pan);
// takes UD(MediaTrack *) returns (bool, maybe double vol, maybe double pan)

static int fGetTrackUIVolPan(lua_State *L)
{
	assertFunctionPointer( GetTrackUIVolPan );

	MediaTrack *mtp = toMediaTrack(L, 1);
	double	vol, pan;

	bool answer = GetTrackUIVolPan(mtp, &vol, &pan);

	lua_pushboolean(L, answer);

	if(answer)
	{
		lua_pushnumber(L, vol);
		lua_pushnumber(L, pan);
		return 3;
	}

	return 1;
}

// int (*TrackFX_GetCount)(MediaTrack *tr);
// takes UD(MediaTrack *) returns int

static int fMediaTrack2int(lua_State *L, int f(MediaTrack *) , char *fname )
{
	assertFunc(f, fname);

	MediaTrack *mtp = toMediaTrack(L, 1);
	int anser =	f(mtp);
	lua_pushnumber(L, anser);
	return 1;
}

#define mkMediaTrack2int(NAME) \
	static int f##NAME(lua_State *L) { return fMediaTrack2int( L , NAME , #NAME ); }

mkMediaTrack2int(TrackFX_GetCount);
mkMediaTrack2int(GetTrackNumMediaItems);

// int (*TrackFX_GetNumParams)(MediaTrack *tr, int fx);
// takes (UD(MediaTrack *), int) returns int

static int fMediaTrackInt2int(lua_State *L, int f(MediaTrack *,int) , char *fname )
{
	assertFunc(f, fname);

	MediaTrack *mtp = toMediaTrack(L, 1);
	int arg2 = luaL_checkinteger(L, 2);
	int anser =	f(mtp, arg2);
	lua_pushnumber(L, anser);
	return 1;
}

#define mkMediaTrackInt2int(NAME) \
	static int f##NAME(lua_State *L) { return fMediaTrackInt2int( L , NAME , #NAME); }

mkMediaTrackInt2int(TrackFX_GetNumParams);

// bool (*TrackFX_GetFXName)(MediaTrack *tr, int fx, char *buf, int buflen);
// takes UD(MediaTrack *), int 
// returns string or nothing

static int fTrackFX_GetFXName(lua_State *L)
{
	assertFunctionPointer( TrackFX_GetFXName );

	char buf[BUFSIZE];
	MediaTrack *mtp = toMediaTrack(L, 1);
	int fx = luaL_checkinteger(L, 2);

	bool anser = TrackFX_GetFXName(mtp, fx, buf, BUFSIZE);
	if(anser)
	{
		lua_pushstring(L, buf);
		return 1;
	}

	return 0;
}

// double (*TrackFX_GetParam)(MediaTrack *tr, int fx, int param, double *minval, double *maxval);
// takes ud(MediaTrack *), int fx, int param
// returns double curval, double minval, double maxval

static int fTrackFX_GetParam(lua_State *L)
{
	assertFunctionPointer( TrackFX_GetParam );

	MediaTrack *mtp = toMediaTrack(L, 1);
	int fx = luaL_checkinteger(L, 2);
	int param = luaL_checkinteger(L, 3);

	double curval, minval, maxval;
	curval = TrackFX_GetParam(mtp, fx, param, &minval, &maxval);

	lua_pushnumber(L, curval);
	lua_pushnumber(L, minval);
	lua_pushnumber(L, maxval);
	return 3;
}

// bool (*TrackFX_SetParam)(MediaTrack *tr, int fx, int param, double val);
// takes (UD(MediaTrack *), int fx, int param, double val)
// returns bool

static int fTrackFX_SetParam(lua_State *L)
{
	assertFunctionPointer( TrackFX_SetParam );

	MediaTrack *mtp = toMediaTrack(L, 1);
	int fx = luaL_checkinteger(L, 2);
	int param = luaL_checkinteger(L, 3);
	double val = luaL_checknumber(L, 4);

	bool anser = TrackFX_SetParam(mtp, fx, param, val);
	lua_pushboolean(L, anser);
	return 1;
}

// bool (*TrackFX_GetParamName)(MediaTrack *tr, int fx, int param, char *buf, int buflen);
// takes ud(MediaTrack *), int fx, int param 
// returns string or nothing

static int fTrackFX_GetParamName(lua_State *L)
{
	assertFunctionPointer( TrackFX_GetParamName );

	char buf[BUFSIZE];
	MediaTrack *mtp = toMediaTrack(L, 1);
	int fx = luaL_checkinteger(L, 2);
	int param = luaL_checkinteger(L, 3);

	bool anser = TrackFX_GetParamName(mtp, fx, param, buf, BUFSIZE);
	if(anser)
	{
		lua_pushstring(L, buf);
		return 1;
	}

	return 0;
}

// bool (*TrackFX_FormatParamValue)(MediaTrack *tr, int fx, int param, double val, char *buf, int buflen);
// takes ud(MT *), int fx, int param, double val
// returns string or nothing

static int fTrackFX_FormatParamValue(lua_State *L)
{
	assertFunctionPointer( TrackFX_FormatParamValue );

	char buf[BUFSIZE];
	MediaTrack *mtp = toMediaTrack(L, 1);
	int fx = luaL_checkinteger(L, 2);
	int param = luaL_checkinteger(L, 3);
	double val = luaL_checknumber(L, 4);

	bool anser = TrackFX_FormatParamValue(mtp, fx, param, val, buf, BUFSIZE);
	if(anser)
	{
		lua_pushstring(L, buf);
		return 1;
	}

	return 0;
}

// bool (*EnumPitchShiftModes)(int idx, char **out);
// takes int idx
// returns string or nothing

static int fEnumPitchShiftModes(lua_State *L)
{
	assertFunctionPointer( EnumPitchShiftModes );

	char *out = NULL;
	int idx = luaL_checkinteger(L, 1);
	bool anser = EnumPitchShiftModes(idx, &out);
	if(anser)
	{
		lua_pushstring(L, out);
		return 1;
	}

	return 0;
}

// const char *(*EnumPitchShiftSubModes)(int idx, int submode);
// takes (int,int)
// returns string or nothing

static int fEnumPitchShiftSubModes(lua_State *L)
{
	assertFunctionPointer( EnumPitchShiftSubModes );

	int idx = luaL_checkinteger(L, 1);
	int submode = luaL_checkinteger(L, 2);

	const char *str = EnumPitchShiftSubModes(idx, submode);
	if(str)
	{
		lua_pushstring(L, str);
		return 1;
	}

	return 0;
}

// bool f(int arg, char* b, int bs)
// takes int
// returns string or nothing

static int fint2optstring(lua_State *L, bool f(int, char *, int) , char *fname )
{
	assertFunc(f, fname);

	char buf[BUFSIZE];
	int arg1 = luaL_checkinteger(L, 1);
	bool answer = f(arg1, buf, BUFSIZE);
	if(answer)
	{
		lua_pushstring(L, buf);
		return 1;
	}
	return 0;
}

#define mkint2optstring(NAME) \
	static int f##NAME(lua_State *L) { return fint2optstring( L , NAME , #NAME ); }

mkint2optstring(GetMIDIOutputName);
mkint2optstring(GetMIDIInputName);

// void (*format_timestr)(double tpos, char *buf, int buflen); 
// takes double returns string

static int fformat_timestr(lua_State *L)
{
	assertFunctionPointer( format_timestr );

	char buf[BUFSIZE];
	double tpos = luaL_checknumber(L, 1);
	format_timestr(tpos, buf, BUFSIZE);
	lua_pushstring(L, buf);
	return 1;
}

//	void (*format_timestr_pos)(double tpos, char *buf, int buflen, int modeoverride);
// takes (double tpos,int modover) returns string

static int fformat_timestr_pos(lua_State *L)
{
	assertFunctionPointer( format_timestr_pos );

	char buf[BUFSIZE];
	double tpos = luaL_checknumber(L, 1);
	int modover = luaL_checkinteger(L, 2);
	format_timestr_pos(tpos, buf, BUFSIZE, modover);
	lua_pushstring(L, buf);
	return 1;
}

//	double (*parse_timestr_pos)(const char *buf, int modeoverride);
// takes string,int returns double

static int fparse_timestr_pos(lua_State *L)
{
	assertFunctionPointer( parse_timestr_pos );

	const char *buf = luaL_checkstring(L, 1);
	int modover = luaL_checkinteger(L, 2);

	double anser = parse_timestr_pos(buf, modover);
	lua_pushnumber(L, anser);
	return 1;
}

// void (*format_timestr_len)(double tpos, char *buf, int buflen, double offset, int modeoverride); // offset is start of where the "length" will be calculated from
// takes (double tpos, double offset, int modover) returns string

static int fformat_timestr_len(lua_State *L)
{
	assertFunctionPointer( format_timestr_len );

	char buf[BUFSIZE];
	double tpos = luaL_checknumber(L, 1);
	double offset = luaL_checknumber(L, 2);
	int modover = luaL_checkinteger(L, 3);

	format_timestr_len(tpos, buf, BUFSIZE, offset, modover);
	lua_pushstring(L, buf);
	return 1;
}

// double (*parse_timestr_len)(const char *buf, double offset, int modeoverride);
// takes (string,double,int) returns double

static int fparse_timestr_len(lua_State *L)
{
	assertFunctionPointer( parse_timestr_len );

	const char *str = luaL_checkstring(L, 1);
	double offset = luaL_checknumber(L, 2);
	int modover = luaL_checkinteger(L, 3);
	double anser = parse_timestr_len(str, offset, modover);
	lua_pushnumber(L, anser);
	return 1;
}

// double (*parse_timestr)(const char *buf); 
// takes string returns double

static int fparse_timestr(lua_State *L)
{
	assertFunctionPointer( parse_timestr );

	const char *str = luaL_checkstring(L, 1);
	double anser = parse_timestr(str);
	lua_pushnumber(L, anser);
	return 1;
}

//	 void (*Main_OnCommand)(int command, int flag);
// takes int,int returns void

static int fMain_OnCommand(lua_State *L)
{
	assertFunctionPointer( Main_OnCommand );

	int arg1 = luaL_checkinteger(L, 1);
	int arg2 = luaL_checkinteger(L, 2);

	Main_OnCommand(arg1, arg2);

	return 0;
}

//	 void (*Main_openProject)(char *name);
// takes string returns void

static int fMain_openProject(lua_State *L)
{
	assertFunctionPointer( Main_openProject );

	const char *arg1 = luaL_checkstring(L, 1);
	Main_openProject((char *)arg1);

	return 0;
}

// void *(*GetSetMediaTrackInfo)(MediaTrack *tr, const char *parmname, void *setNewValue);
// takes ( UD(MediaTrack *), string parmname, maybe UD(void *) ) returns LUD(void *)

static int fGetSetMediaTrackInfo(lua_State *L)
{
	assertFunctionPointer( GetSetMediaTrackInfo );

	MediaTrack *mtp = toMediaTrack(L, 1);
	const char *name = luaL_checkstring(L, 2);

	void *valp = NULL;
	if(lua_gettop(L) >= 3)
	{
		valp = tovoid(L, 3);
	}

	void *answer = GetSetMediaTrackInfo(mtp, name, valp);

	pushvoid(L, answer);

	return 1;
}

// void *(*GetSetTrackSendInfo)(MediaTrack *tr, int category, int sendidx, const char *parmname, void *setNewValue);
// takes (mtp,cat,sidx,p_name, maybe value) returns UD(void *)

static int fGetSetTrackSendInfo(lua_State *L)
{
	assertFunctionPointer( GetSetTrackSendInfo );

	MediaTrack *mtp = toMediaTrack(L, 1);
	int category = luaL_checkinteger(L, 2);
	int sendidx = luaL_checkinteger(L, 3);

	const char *p_name = luaL_checkstring(L, 4);

	void *valp = NULL;
	if(lua_gettop(L) >= 5)
	{
		valp = tovoid(L, 5);
	}

	void *answer = GetSetTrackSendInfo(mtp, category, 
		sendidx, p_name, valp);
	pushvoid(L, answer);
	return 1;
}

// bool (*DeleteTrackMediaItem)(MediaTrack *tr, MediaItem *it);
// takes (mtp, mip) returns bool

static int fDeleteTrackMediaItem(lua_State *L)
{
	assertFunctionPointer( DeleteTrackMediaItem );

	MediaTrack *mtp = toMediaTrack(L, 1);
	MediaItem *mip = toMediaItem(L, 2);
	bool answer = DeleteTrackMediaItem(mtp, mip);
	lua_pushboolean(L, answer);
	return 1;
}

// MediaItem *(*GetTrackMediaItem)(MediaTrack *tr, int itemidx);
// take ( mtp, itemidx ) returns UD(mip)

static int fGetTrackMediaItem(lua_State *L)
{
	assertFunctionPointer( GetTrackMediaItem );

	MediaTrack *mtp = toMediaTrack(L, 1);
	int itemidx = luaL_checkinteger(L, 2);
	MediaItem *mip = GetTrackMediaItem(mtp, itemidx);
	pushMediaItem(L, mip);
	return 1;
}

// MediaItem_Take *(*GetMediaItemTake)(MediaItem *item, int tk);
// takes ( mip, int tk ) returns UD(MediaItem_Take *)

static int fGetMediaItemTake(lua_State *L)
{
	assertFunctionPointer( GetMediaItemTake );

	MediaItem *mip = toMediaItem(L, 1);
	int tk = luaL_checkinteger(L, 2);
	MediaItem_Take *mitp = GetMediaItemTake(mip, tk);
	pushMediaItem_Take(L, mitp);
	return 1;
}

// int (*GetMediaItemNumTakes)(MediaItem *item);
// takes mip returns int

static int fGetMediaItemNumTakes(lua_State *L)
{
	assertFunctionPointer( GetMediaItemNumTakes );

	MediaItem *mip = toMediaItem(L, 1);
	int answer = GetMediaItemNumTakes(mip);
	lua_pushinteger(L, answer);
	return 1;
}

//	getset thingies!!
// void *(*GetSetMediaItemInfo)(MediaItem *item, const char *parmname, void *setNewValue);
// takes ( mip, p_name, maybe UD(void *) ) returns UD(void *)

static int fGetSetMediaItemInfo(lua_State *L)
{
	assertFunctionPointer( GetSetMediaItemInfo );

	MediaItem *mip = toMediaItem(L, 1);
	const char *p_name = luaL_checkstring(L, 2);

	void *valp = NULL;		// default is NULL means "Get"
	if(lua_gettop(L) >= 3)
	{
		valp = tovoid(L, 3);
	}
	void *answer = GetSetMediaItemInfo(mip, p_name, valp);
	pushvoid(L, answer);
	return 1;
}

// void *(*GetSetMediaItemTakeInfo)(MediaItem_Take *tk, const char *parmname, void *setNewValue);
// takes ( UD(mitp), p_name, maybe UD(void *) value

static int fGetSetMediaItemTakeInfo(lua_State *L)
{
	assertFunctionPointer( GetSetMediaItemTakeInfo );

	MediaItem_Take *tkp = toMediaItem_Take(L, 1);
	const char *p_name = luaL_checkstring(L, 2);

	void *valp = NULL;		// default is NULL means "Get"
	if(lua_gettop(L) >= 3)
	{
		valp = tovoid(L, 3);
	}
	void *answer = GetSetMediaItemTakeInfo(tkp, p_name, valp);
	pushvoid(L, answer);
	return 1;
}

// MediaItem *(*AddMediaItemToTrack)(MediaTrack *tr); // creates a new media item.
// takes mtp returns mip

static int fAddMediaItemToTrack(lua_State *L)
{
	assertFunctionPointer( AddMediaItemToTrack );

	MediaTrack *mtp = toMediaTrack(L, 1);
	MediaItem *mip = AddMediaItemToTrack(mtp);
	pushMediaItem(L, mip);
	return 1;
}

// MediaItem_Take *(*AddTakeToMediaItem)(MediaItem *item); // creates a new take in an item
// takes mip returns miTp

static int fAddTakeToMediaItem(lua_State *L)
{
	assertFunctionPointer( AddTakeToMediaItem );

	MediaItem *mip = toMediaItem(L, 1);
	MediaItem_Take *mitp = AddTakeToMediaItem(mip);
	pushMediaItem_Take(L, mitp);
	return 1;
}

// bool (*MoveMediaItemToTrack)(MediaItem *item, MediaTrack *desttr); // returns TRUE if move succeeded

static int fMoveMediaItemToTrack(lua_State *L)
{
	assertFunctionPointer( MoveMediaItemToTrack );

	MediaItem *mip = toMediaItem(L, 1);
	MediaTrack *mtp = toMediaTrack(L, 2);

	bool answer = MoveMediaItemToTrack(mip, mtp);
	lua_pushboolean(L, answer);
	return 1;
}

// PCM_source functions:

// Creates a PCM_source from a ISimpleMediaDecoder
// (if fn is non-null, it will open the file in dec)
// PCM_source *(*PCM_Source_CreateFromSimple)(ISimpleMediaDecoder *dec, const char *fn);

//static int fPCM_Source_CreateFromSimple uhh I need an ISimpleMediaDecoder

// Create a PCM_source from a filename
// PCM_source *(*PCM_Source_CreateFromFile)(const char *filename);

// takes string returns UD(PCM_source *)

static int fPCM_Source_CreateFromFile(lua_State *L)
{
	assertFunctionPointer( PCM_Source_CreateFromFile );

	const char *filename = luaL_checkstring(L, 1);
	PCM_source *psp = PCM_Source_CreateFromFile(filename);
	pushPCM_source(L, psp);
	return 1;
}

  // allows you to create a PCM_soucre from filename, and override MIDI files being imported as midi-events.
// PCM_source *PCM_Source_CreateFromFileEx(const char *filename, bool forcenoMidiImp); 
// takes ( string, bool ) returns UD(PCM_source *)

// there is a syntax error in the header file I do think I gotsta replace it
// REAPER_PLUGIN_DECLARE_APIFUNCS PCM_source *PCM_Source_CreateFromFileEx(const char *filename, bool forcenoMidiImp); 

REAPER_PLUGIN_DECLARE_APIFUNCS PCM_source * (*ps_cffe)(const char *filename, bool forcenoMidiImp); 

static int fPCM_Source_CreateFromFileEx(lua_State *L)
{
	assertFunc( ps_cffe , "PCM_Source_CreateFromFileEx" );

	const char *filename = luaL_checkstring(L, 1);
	bool forcenoMidiImp = luaL_checkboolean(L, 2);
	PCM_source *psp = ps_cffe(filename, forcenoMidiImp);
	pushPCM_source(L, psp);
	return 1;
}

// Create a PCM_source from a "type" (use this if you're going to load its state via LoadState and a ProjectStateContext
// valid types include "WAVE", "MIDI", or whatever plug-ins define as well.
// PCM_source *(*PCM_Source_CreateFromType)(const char *sourcetype);

static int fPCM_Source_CreateFromType(lua_State *L)
{
	assertFunctionPointer( PCM_Source_CreateFromType );

	const char *sourcetype = luaL_checkstring(L, 1);
	PCM_source *psp = PCM_Source_CreateFromType(sourcetype);
	pushPCM_source(L, psp);
	return 1;
}

// Create a MIDI file PCM_sink.
// currently cfg and cfgl are ignored.
// bpm is BPM, div is ticks/QN to use.

// PCM_sink *(*PCM_Sink_CreateMIDIFile)
//			(const char *filename, const char *cfg, int cfgl,
//				double bpm, int div); 

// takes string, string, int, double, int
// returns reaper.PCM_sink

static int fPCM_Sink_CreateMIDIFile(lua_State *L)
{
	assertFunctionPointer( PCM_Sink_CreateMIDIFile );

	const char *filename = luaL_checkstring(L, 1);
	const char *cfg = luaL_checkstring(L, 2);
	int cfg1 = luaL_checkinteger(L, 3);
	double bpm = luaL_checknumber(L, 4);
	int div = luaL_checkinteger(L, 5);

	PCM_sink *psp = PCM_Sink_CreateMIDIFile(filename, cfg, cfg1, bpm, div);

	pushPCM_sink(L, psp);
	return 1;
}

// Enumerate available PCM sinks. returns 0 when finished (start with id=0, etc). descstr gets set to description of type.
// unsigned int (*PCM_Sink_Enum)(int id, char **descstr);

static int fPCM_Sink_Enum(lua_State *L)
{
	assertFunctionPointer( PCM_Sink_Enum );

	char *desc;
	int id = luaL_checkinteger(L, 1);

	int answer = PCM_Sink_Enum(id, &desc);

	lua_pushinteger(L, answer);
	if(answer)
	{
		lua_pushstring(L, desc);
		return 2;
	}

	return 1;
}

// Create a PCM sink (configured with cfg/cfgl). 
// In general, the first 4 bytes of "cfg" represent the sink type (i.e. (REAPER_FOURCC('W','A','V','E'), 
// any additional data is type-specific configuration.
// if buildpeaks is TRUE then the appropriate .reapeaks file is created on the fly.
// Currently nch must be 1 or 2 for most sinks.

// PCM_sink *(*PCM_Sink_Create)(const char *filename, const char *cfg,
//					int cfgl, int nch, int srate, bool buildpeaks);

// ps = reaper.PCM_Sink_CreateMIDIFile("blah.mid", "duh", 1, 111, 8)

static int fPCM_Sink_Create(lua_State *L)
{
	assertFunctionPointer( PCM_Sink_Create );

	const char *filename	= luaL_checkstring(L, 1);
	const char *cfg			= luaL_checkstring(L, 2);
	int cfg1				= luaL_checkinteger(L, 3);
	int nch					= luaL_checkinteger(L, 4);
	int srate				= luaL_checkinteger(L, 5);
	bool buildpeaks			= luaL_checkboolean(L, 6);

	PCM_sink *psp = PCM_Sink_Create(filename, cfg, cfg1, nch, srate, buildpeaks);

	pushPCM_sink(L, psp);
	return 1;
}

// takes void returns table with strings in numeric indices starting with 1
// so that ipairs can traverse it without leaving off the first one!

static int fvoidtolist(lua_State *L, char * f() , char *fname )
{
	assertFunc( f , fname );

	char *fliszt = f();
	lua_newtable(L);

	char *lp = fliszt;
	int sn = 1;
	while(*lp)
	{
		lua_pushnumber(L, sn);
		lua_pushstring(L, lp);
		lua_settable(L, -3);

		lp += strlen(lp) + 1;
		sn++;
	}
	return 1;
}

// this returns a double-NULL terminated list of importable media files, suitable for passing to GetOpenFileName() etc
// includes *.* (All Files), too...
// REAPER_PLUGIN_DECLARE_APIFUNCS char *(*plugin_getFilterList)();

#define mkvoidtolist(NAME)	\
	static int f##NAME(lua_State *L) { return fvoidtolist( L , NAME , #NAME ); }

mkvoidtolist(plugin_getFilterList);
mkvoidtolist(plugin_getImportableProjectFilterList);

//REAPER_PLUGIN_DECLARE_APIFUNCS midi_Input *(*GetMidiInput)(int idx);
//REAPER_PLUGIN_DECLARE_APIFUNCS midi_Output *(*GetMidiOutput)(int idx);
// takes int returns luna{midi_Input}

static int fGetMidiInput(lua_State *L)
{
	assertFunctionPointer( GetMidiInput );

	int idx = luaL_checkinteger(L, 1);
	midi_Input *mip = GetMidiInput(idx);
	pushmidi_Input(L, mip);
	return 1;
}

// REAPER_PLUGIN_DECLARE_APIFUNCS midi_Input *(*CreateMIDIInput)(int dev);

static int fCreateMIDIInput(lua_State *L)
{
	assertFunctionPointer( CreateMIDIInput );

	int dev = luaL_checkinteger(L, 1);
	midi_Input *mip = CreateMIDIInput(dev);
	pushmidi_Input(L, mip);
	return 1;
}

// midi_Output *(*CreateMIDIOutput)(int dev, bool streamMode, int *msoffset100); 
// takes ( int dev, vool streamMode )
// returns ( luna{midi_Output}, maybe msoffset100 )

static int fCreateMIDIOutput(lua_State *L)
{
	assertFunctionPointer( CreateMIDIOutput );

// msoffset used if streamMode is set, point to a persistent variable that can change and reflects added delay to output in 100ths of a millisecond.
	static int msoffset100 = 0;	// I think I am totally misinterpreting this!

	int dev = luaL_checkinteger(L, 1);
	bool streamMode = luaL_checkboolean(L, 2);

	midi_Output *mop = CreateMIDIOutput(dev, streamMode, &msoffset100);

	pushmidi_Output(L, mop);
	if(streamMode)
	{
		lua_pushnumber(L, msoffset100);
		return 2;
	}

	return 1;
}

// void (*GetSet_LoopTimeRange)(bool isSet, bool isLoop, double *start, double *end, bool allowautoseek);

static int fGet_LoopTimeRange(lua_State *L)
{
	double start = 0;
	double end = 0;

	assertFunctionPointer( GetSet_LoopTimeRange );

	bool isloop = luaL_checkboolean(L, 1);
	bool allowautoseek = luaL_checkboolean(L, 2);

	GetSet_LoopTimeRange( false, isloop, &start, &end, allowautoseek );

	lua_pushnumber(L, start);
	lua_pushnumber(L, end);

	return 2;
}

// void (*GetSet_LoopTimeRange)(bool isSet, bool isLoop, double *start, double *end, bool allowautoseek);

static int fSet_LoopTimeRange(lua_State *L)
{
	assertFunctionPointer( GetSet_LoopTimeRange );

	bool isloop = luaL_checkboolean(L, 1);

	double start = luaL_checknumber(L, 2);
	double end = luaL_checknumber(L, 3);

	bool allowautoseek = luaL_checkboolean(L, 4);

	GetSet_LoopTimeRange( true, isloop, &start, &end, allowautoseek );

	lua_pushnumber(L, start);
	lua_pushnumber(L, end);

	return 2;
}

// int (*EnumProjectMarkers)(int idx, bool *isrgn, 
//							double *pos, double *rgnend, 
//                          char **name, int *markrgnindexnumber);

// NOW it takes an index
// returns table
//			{index, nextidx, isrgn, pos, name, markrgnindexnumber,
//			    and if isrgn rgnend			}

static int fEnumProjectMarkers(lua_State *L)
{
	assertFunctionPointer( EnumProjectMarkers );

	int idx = luaL_checkinteger(L, 1);

	bool isrgn = false;
	double pos = 0;
	double rgnend = 0;
	char *name ="";
	int markrgnindexnumber = 0;
	
	int nextidx = EnumProjectMarkers(idx, &isrgn, &pos, 
									&rgnend, &name, &markrgnindexnumber);
	if(nextidx)
	{
		lua_newtable(L);
		reaperNumberConstant(L, "index", idx);
		reaperNumberConstant(L, "nextidx", nextidx);
		reaperBoolConstant(L, "isrgn", isrgn);
		reaperNumberConstant(L, "pos", pos);
		reaperStringConstant(L, "name", name);
		reaperNumberConstant(L, "markrgnindexnumber", markrgnindexnumber);
		if(isrgn)
		{
			reaperNumberConstant(L, "rgnend", rgnend);
		}

		return 1;
	}

	return 0;
}

// void (*Undo_OnStateChange)(const char *descchange, int whichStates, int trackparm/*=-1*/);

static int fUndo_OnStateChange(lua_State *L)
{
	assertFunctionPointer( Undo_OnStateChange );

	const char *descchange = luaL_checkstring(L, 1);
	int whichStates = luaL_checkinteger(L, 2);
	int trackparm = -1;

	if(lua_gettop(L) >= 3)
	{
		trackparm = luaL_checkinteger(L, 3);
	}

//	Undo_OnStateChange(descchange, whichStates, trackparm);

	return 0;
}

// hey dummy this should be a mkvoid2void incarnation

// void (*Undo_BeginBlock)(); // call to start a new "block"
static int fUndo_BeginBlock(lua_State *L)
{
	assertFunctionPointer( Undo_BeginBlock );
	Undo_BeginBlock();
	return 0;
}
// void (*Undo_EndBlock)(const char *descchange, int extraflags); // call to end the block, with extra flags if any, and a description

static int fUndo_EndBlock(lua_State *L)
{
	assertFunctionPointer( Undo_EndBlock );

	const char *descchange = luaL_checkstring(L, 1);
	int extraflags = 0;

	if(lua_gettop(L) >= 2)
	{
		extraflags = luaL_checkinteger(L, 2);
	}

	Undo_EndBlock(descchange, extraflags);

	return 0;
}

static int fplugin_getapi(lua_State *L)
{
	assertFunctionPointer( plugin_getapi );

	const char *fname = luaL_checkstring(L, 1);
	void *fptr = plugin_getapi(fname);
	pushvoid(L, fptr);

	return 1;
}

// Yummy utility functions!

int flkeydown(WORD key)
{
	INPUT	dai = {0};

	dai.type = INPUT_KEYBOARD;
	dai.ki.wVk = key;

	UINT rv = SendInput(1, &dai, sizeof(dai) );
	return rv;
}

int flkeyup(WORD key)
{
	INPUT	dai = {0};

	dai.type = INPUT_KEYBOARD;
	dai.ki.wVk = key;
	dai.ki.dwFlags = KEYEVENTF_KEYUP;

	UINT rv = SendInput(1, &dai, sizeof(dai) );
	return rv;
}

int keystack(WORD *wp, int n)
{
	UINT	rv = 0;
	for(int i=0; i < n; i++)
	{
		rv += flkeydown(wp[i]);
	}	
	for(int i=n-1; i >=0; i--)
	{
		rv += flkeyup(wp[i]);
	}

	return rv;
}

void PrepReaperWindow(void)
{
	HWND hw = GetMainHwnd();
	BringWindowToTop(hw);
	SetFocus(hw);
	BYTE kstate[256] = {0};
	SetKeyboardState(kstate);
}

static int fkeystack(lua_State *L)
{
	PrepReaperWindow();

	int n = lua_gettop(L);  /* number of arguments */
	WORD *ks = (WORD *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, n * sizeof(WORD) );
	for(int i=1; i<=n; i++)
	{
		WORD rk = luaL_checkinteger(L, i);
		ks[i-1] = rk;
	}

	UINT rv = keystack(ks, n);

	HeapFree(GetProcessHeap(), 0, ks);

	lua_pushnumber(L, rv);
	return 1;
}

// this is supposed to type text for the user
// frinstance to type a file name or maybe an effect filter string for the user

static int fsendstring(lua_State *L)
{
	PrepReaperWindow();

	UINT	rv = 0;
	const char *ts = luaL_checkstring(L, 1);
	size_t sts = strlen(ts);

	for(size_t i=0; i < sts; i++)
	{
		WORD rk = ts[i];
		rv += keystack(&rk, 1);
	}

	lua_pushnumber(L, rv);
	return 1;
}

// make the main Reaper window redraw so we can see what we're doing
// (is this useful anymore? we're puttin' it back in to see if it is!)

static int fRedraw(lua_State *L)
{
	assertFunctionPointer( GetMainHwnd );

	HWND hw = GetMainHwnd();
	RedrawWindow(hw, NULL, NULL, RDW_UPDATENOW);
	return 0;
}

static int fsleep(lua_State *L)
{
	int ms = 0;
	if(lua_gettop(L) > 0)
	{
		ms = luaL_checkinteger(L, 1);
	}
		
	Sleep(ms);
	return 0;
}

// takes a string returns a table

static int fgetmetatable(lua_State *L)
{
	const char *metname = luaL_checkstring(L, 1);
	luaL_getmetatable(L, metname);
	return 1;
}

reaper_plugin_info_t *grec;

static int fgetfunc(lua_State *L)
{
	if(grec)
	{
		const char *fname = luaL_checkstring(L, 1);
		void *fptr = grec->GetFunc(fname);
		pushvoid(L, fptr);

		return 1;
	}

	return 0;
}

static int fregister(lua_State *L)
{
	if(grec)
	{
		const char *fname = luaL_checkstring(L, 1);
		void *vptr = tovoid(L, 2);

		int success = grec->Register(fname, vptr);
		lua_pushinteger(L, success);

		return 1;
	}

	return 0;
}

static int makeMidiEvent1(lua_State *L)
{
	int frame_offset = luaL_checkinteger(L, 1);
	int statusByte = luaL_checkinteger(L, 2);
	int data1Byte = luaL_checkinteger(L, 3);
	int data2Byte = luaL_checkinteger(L, 4);

	lua_pushnumber(L, 1);		// stack holds index n=1 at which to save the following UD

	MIDI_event_t *metp = (MIDI_event_t *) lua_newuserdata(L, sizeof(MIDI_event_t));
	metp->frame_offset = frame_offset;
	metp->size = 3;
	metp->midi_message[0] = statusByte;
	metp->midi_message[1] = data1Byte;
	metp->midi_message[2] = data2Byte;	// n=1 userdata
										
	int rv = pushMIDI_event_t(L, metp); // n=1 userdata {bridge}
	lua_insert(L, -3);					
										// {bridge} n=1 userdata
	lua_settable(L, -3);				// {bridge[0]=userdata ptr to 
										//  bridge[1]=userdata of m_e_t}
	return 1;
}

static int makeMidiEvent2(lua_State *L)
{
	int frame_offset = luaL_checkinteger(L, 1);
	int statusByte = luaL_checkinteger(L, 2);
	const char *message = luaL_checkstring(L, 3);
	int size = lua_strlen(L, 3);

	int metsize = size + sizeof(int) + sizeof(int);

	MIDI_event_t *metp = (MIDI_event_t *) lua_newuserdata(L, metsize);

	metp->frame_offset = frame_offset;
	metp->size = size;
	memcpy(metp->midi_message, message, size);

	return 1;
}

static int fmakeMidiEvent(lua_State *L)
{
	if( lua_type(L, 3) == LUA_TSTRING )
	{
		return makeMidiEvent2(L);
	}

	return makeMidiEvent1(L);
}

static int fmakeMidiEventList(lua_State *L)
{
	MIDI_eventlist *pmel = (MIDI_eventlist *) new ReaperMidiEventList();//(MIDI_eventlist *)lua_newuserdata(L, melsize);
	pushMIDI_eventlist(L, pmel);
	return 1;
}

#if 0
// used to update MIDI sources with new events during recording
typedef struct
{
  double global_time;
  double global_item_time;
  double srate;
  int length; // length in samples
  int overwritemode; // 0=overdub, 1=replace, -1 = literal (do nothing just add)
  MIDI_eventlist *events;
  double item_playrate;

  double latency;

} midi_realtime_write_struct_t;

pso = reaper.PCM_Source_CreateFromType("MIDI")
mel = reaper.makeMidiEventList()
mrt = reaper.makeMidiRealtime(mel)
#endif

static int fmakeMidiRealtime(lua_State *L)
{
	MIDI_eventlist *evl = (MIDI_eventlist *) toReaperObject(L, 1);

	midi_realtime_write_struct_t *mrp = (midi_realtime_write_struct_t *)
				lua_newuserdata(L, sizeof(midi_realtime_write_struct_t));

	midi_realtime_write_struct_t *chk = (midi_realtime_write_struct_t *)
				lua_touserdata(L, -1);

	mrp->events = evl;
	mrp->global_time = 0;
	mrp->global_item_time = 0;
	mrp->srate = 48000;
	mrp->length = 48000 * 5 * 60;
	mrp->overwritemode = 1;
	mrp->item_playrate = 1;
	mrp->latency = 0;

	printf("", chk);

	return 1;
}

// a no-printf-ing failer:

int checkFP(void *varp)
{
	if(varp)
		return false;

	return true;		// an error
}

int setFunctionPointer(reaper_plugin_info_t *rec, void **varp, char *varn)
{
	assert(rec);
	assert(varp);
	assert(varn);

	*varp = rec->GetFunc(varn);
	
	return checkFP(varp);
}

#define sfp(FNAME)									\
	setFunctionPointer( rec ,						\
						((void **) & ##FNAME ) ,	\
						#FNAME  );

// Now fetch the pointers:

void initFunctionPointers(reaper_plugin_info_t *rec)
{
	grec = rec;
#if 0	// little test jig
		sfp(GetMainHwnd);
#else
// first we do the special case of the single repaired varble (there was a tiny buglet in the header file so we made our own repaired one)
	*((void **)&ps_cffe) = rec->GetFunc("PCM_Source_CreateFromFileEx");

// Now all the others are variable name and function text name are identical:
	sfp(plugin_getapi);
	sfp(GetMidiInput);

	sfp(CreateMIDIInput);
	sfp(CreateMIDIOutput);

	sfp(GetSet_LoopTimeRange);
	sfp(EnumProjectMarkers);
	sfp(Undo_OnStateChange);
	sfp(Undo_BeginBlock);
	sfp(Undo_EndBlock);

	sfp(PCM_Source_CreateFromSimple);
	sfp(PCM_Source_CreateFromFile);

	sfp(PCM_Source_CreateFromType);

	sfp(PCM_Sink_CreateMIDIFile);
	sfp(PCM_Sink_Enum);
	sfp(PCM_Sink_Create);
	

	sfp(plugin_getFilterList);
	sfp(plugin_getImportableProjectFilterList);



	sfp(DeleteTrackMediaItem);
	sfp(GetTrackMediaItem);
	sfp(GetMediaItemTake);
	sfp(GetMediaItemNumTakes);
	sfp(GetSetMediaItemInfo);
	sfp(GetSetMediaItemTakeInfo);
	sfp(AddMediaItemToTrack);
	sfp(AddTakeToMediaItem);
	sfp(MoveMediaItemToTrack);
	
//	 void (*Main_openProject)(char *name);
// takes (char *) returns void
	sfp(Main_openProject);

//	 void (*Main_OnCommand)(int command, int flag);
// takes (int,int) returns void
	sfp(Main_OnCommand);


	sfp(EnumPitchShiftModes);
	sfp(EnumPitchShiftSubModes);

	sfp(GetMIDIOutputName);
	sfp(GetMIDIInputName);

	sfp(format_timestr);
	sfp(format_timestr_pos);
	sfp(parse_timestr_pos);
	sfp(format_timestr_len);
	sfp(parse_timestr_len);
	sfp(parse_timestr);

	sfp(GetSetMediaTrackInfo);
	sfp(GetSetTrackSendInfo);


	// takes void returns void
	sfp(UpdateTimeline);
	sfp(ClearAllRecArmed);
	sfp(ClearPeakCache);
	sfp(TrackList_UpdateAllExternalSurfaces);
	sfp(CSurf_SetTrackListChange);

	sfp(CSurf_OnPlay);
	sfp(CSurf_OnStop);
	sfp(CSurf_OnRecord);
	sfp(CSurf_GoStart);
	sfp(CSurf_GoEnd);
	sfp(CSurf_ResetAllCachedVolPanStates);

	// takes bool returns void
	sfp(MuteAllTracks);
	sfp(TrackList_AdjustWindows);
	sfp(CSurf_FlushUndo);

	// takes int returns void
	sfp(SoloAllTracks);
	sfp(BypassFxAllTracks);
	sfp(Main_UpdateLoopInfo);
	sfp(CSurf_OnFwd);
	sfp(CSurf_OnRew);

	// takes int returns int
	sfp(GetSetRepeat);

	// takes (const char*, char *, int) returns void
	sfp(resolve_fn);
	sfp(relative_fn);
	sfp(GetPeakFileName);

	// takes void returns int
	sfp(Audio_IsRunning);
	sfp(Audio_IsPreBuffer);
	sfp(IsInRealTimeAudio);
	sfp(GetMaxMidiInputs);
	sfp(GetMaxMidiOutputs);
	sfp(GetNumTracks);
	sfp(GetMasterMuteSoloFlags);
	sfp(GetPlayState);
	sfp(Undo_DoRedo);
	sfp(Undo_DoUndo);
	sfp(GetNumMIDIInputs);
	sfp(GetNumMIDIOutputs);

// takes void returns double
	sfp(GetPlayPosition);
	sfp(GetPlayPosition2);
	sfp(GetCursorPosition);
	sfp(GetHZoomLevel);

// takes double returns void
	sfp(CSurf_ScrubAmt);

// takes double returns double
	sfp(DB2SLIDER);
	sfp(SLIDER2DB);
	sfp(Master_GetPlayRateAtTime);

// takes (double,double) returns string
	sfp(mkvolpanstr);

// takes (double) returns string
	sfp(mkvolstr);
	sfp(mkpanstr);

	// takes void returns const char *
	sfp(get_ini_file);
	sfp(GetExePath);
	sfp(Undo_CanUndo);
	sfp(Undo_CanRedo);

	// takes int returns const char *
	sfp(Resample_EnumModes);
	sfp(GetOutputChannelName);
	sfp(GetInputChannelName);

	// takes int,int returns int
	sfp(GetColorTheme);

	// takes void returns HWND
	sfp(GetMainHwnd);

	// takes void returns HMENU
//	sfp(GetContextMenus);
	

	// takes (int,bool) returns void
	sfp(SetAutomationMode);
	sfp(CSurf_OnArrow);

	// takes somethings and returns other things

	sfp(get_config_var);
	sfp(GetProjectTimeSignature);
	sfp(InsertMedia);
	sfp(GetProjectPath);
	sfp(IsMediaExtension);
	sfp(GetTrackInfo);
	sfp(parsepanstr);
	sfp(MoveEditCursor);
	sfp(SetEditCurPos);
	sfp(adjustZoom);

	sfp(GetTrackGUID);
	sfp(genGuid);			// why was this commented out?  Testing?
	sfp(guidToString);
	sfp(stringToGuid);
	sfp(InsertTrackAtIndex);

// takes (MediaTrack *) returns void
	sfp(DeleteTrack);
	sfp(CSurf_OnTrackSelection);


	sfp(CSurf_TrackFromID);
	sfp(CSurf_NumTracks);
	sfp(kbd_OnMidiEvent);
	sfp(kbd_OnMidiList);
	sfp(CSurf_OnVolumeChange);
	sfp(CSurf_OnPanChange);
	sfp(CSurf_OnVolumeChange);
	sfp(CSurf_OnVolumeChangeEx);
	sfp(CSurf_OnPanChangeEx);

	sfp(CSurf_OnMuteChange);
	sfp(CSurf_OnSelectedChange);
	sfp(CSurf_OnSoloChange);
	sfp(CSurf_OnFXChange);
	sfp(CSurf_OnRecArmChange);

	sfp(SetTrackAutomationMode);
	sfp(SetTrackSelected);
	sfp(Track_GetPeakInfo);
	sfp(GetTrackUIVolPan);


	// takes UD(MediaTrack *) returns int

	sfp(CSurf_TrackToID);
	sfp(GetTrackAutomationMode);

	sfp(TrackFX_GetCount);
	sfp(TrackFX_GetNumParams);
	sfp(GetTrackNumMediaItems);


	sfp(TrackFX_GetFXName);
	sfp(TrackFX_GetParam);
	sfp(TrackFX_SetParam);
	sfp(TrackFX_GetParamName);
	sfp(TrackFX_FormatParamValue);
#endif
}

#define QUOTEME(x) #x

#define rrentry(NM) \
{	#NM , f##NM }

static const struct luaL_reg reaReg [] =
{
	rrentry(keystack),
	rrentry(sendstring),
	rrentry(sleep),
	rrentry(Redraw),
	rrentry(getmetatable),
	rrentry(getfunc),
	rrentry(makeMidiEvent),
	rrentry(makeMidiEventList),
	rrentry(makeMidiRealtime),

//	rrentry(exit),

	rrentry(plugin_getapi),

	rrentry(GetMidiInput),
	rrentry(CreateMIDIInput),
	rrentry(CreateMIDIOutput),

	rrentry(Get_LoopTimeRange),
	rrentry(Set_LoopTimeRange),
	rrentry(EnumProjectMarkers),
	rrentry(Undo_OnStateChange),
	rrentry(Undo_BeginBlock),
	rrentry(Undo_EndBlock),

	rrentry(PCM_Source_CreateFromFile),
	rrentry(PCM_Source_CreateFromFileEx),
	rrentry(PCM_Source_CreateFromType),

	rrentry(PCM_Sink_CreateMIDIFile),
	rrentry(PCM_Sink_Enum),
	rrentry(PCM_Sink_Create),

	rrentry(plugin_getFilterList),
	rrentry(plugin_getImportableProjectFilterList),

	rrentry(DeleteTrackMediaItem),
	rrentry(GetTrackMediaItem),
	rrentry(GetMediaItemTake),
	rrentry(GetMediaItemNumTakes),
	rrentry(GetSetMediaItemInfo),
	rrentry(GetSetMediaItemTakeInfo),
	rrentry(AddMediaItemToTrack),
	rrentry(AddTakeToMediaItem),
	rrentry(MoveMediaItemToTrack),

	rrentry(Main_openProject),
	rrentry(Main_OnCommand),
	rrentry(GetMIDIOutputName),
	rrentry(GetMIDIInputName),
	rrentry(format_timestr),
	rrentry(format_timestr_pos),
	rrentry(parse_timestr_pos),
	rrentry(format_timestr_len),
	rrentry(parse_timestr_len),
	rrentry(parse_timestr),
	rrentry(CSurf_OnPlay),
	rrentry(CSurf_OnStop),
	rrentry(CSurf_OnRecord),
	rrentry(CSurf_GoStart),
	rrentry(CSurf_GoEnd),
	rrentry(CSurf_ResetAllCachedVolPanStates),
	rrentry(CSurf_OnFwd),
	rrentry(CSurf_OnRew),
	rrentry(GetSetRepeat),
	rrentry(UpdateTimeline),
	rrentry(ClearAllRecArmed),
	rrentry(ClearPeakCache),
	rrentry(TrackList_UpdateAllExternalSurfaces),
	rrentry(CSurf_SetTrackListChange),
	rrentry(MuteAllTracks),

	rrentry(TrackList_AdjustWindows),
	rrentry(CSurf_FlushUndo),
	rrentry(SoloAllTracks),
	rrentry(BypassFxAllTracks),
	rrentry(Main_UpdateLoopInfo),
	rrentry(resolve_fn),
	rrentry(relative_fn),
	rrentry(GetPeakFileName),
	rrentry(Audio_IsRunning),
	rrentry(Audio_IsPreBuffer),

	rrentry(IsInRealTimeAudio),
	rrentry(GetMaxMidiInputs),
	rrentry(GetMaxMidiOutputs),
	rrentry(GetNumTracks),
	rrentry(GetMasterMuteSoloFlags),
	rrentry(GetPlayState),
	rrentry(Undo_DoRedo),
	rrentry(Undo_DoUndo),
	rrentry(GetNumMIDIInputs),
	rrentry(GetNumMIDIOutputs),
	rrentry(GetPlayPosition),
	rrentry(GetPlayPosition2),
	rrentry(GetCursorPosition),
	rrentry(GetHZoomLevel),
	rrentry(CSurf_ScrubAmt),
	rrentry(DB2SLIDER),
	rrentry(SLIDER2DB),
	rrentry(Master_GetPlayRateAtTime),

	rrentry(mkvolpanstr),
	rrentry(mkvolstr),
	rrentry(mkpanstr),
	rrentry(get_ini_file),
	rrentry(GetExePath),
	rrentry(Undo_CanUndo),
	rrentry(Undo_CanRedo),
	rrentry(Resample_EnumModes),
	rrentry(EnumPitchShiftModes),
	rrentry(EnumPitchShiftSubModes),

	rrentry(GetOutputChannelName),
	rrentry(GetInputChannelName),
	rrentry(GetColorTheme),
	rrentry(GetMainHwnd),
// not there yet	rrentry(GetContextMenus),
	rrentry(get_config_var),
	rrentry(GetProjectTimeSignature),
	rrentry(InsertMedia),
	rrentry(GetProjectPath),
	rrentry(IsMediaExtension),

	rrentry(GetTrackInfo),
	rrentry(parsepanstr),
	rrentry(SetAutomationMode),
	rrentry(CSurf_OnArrow),
	rrentry(MoveEditCursor),
	rrentry(SetEditCurPos),
	rrentry(adjustZoom),
	rrentry(CSurf_TrackFromID),
	rrentry(CSurf_TrackToID),

	rrentry(GetTrackAutomationMode),
	rrentry(SetTrackSelected),
	rrentry(Track_GetPeakInfo),
	rrentry(GetTrackUIVolPan),
	rrentry(CSurf_NumTracks),

	rrentry(kbd_OnMidiList),
	rrentry(kbd_OnMidiEvent),

	rrentry(CSurf_OnVolumeChange),
	rrentry(CSurf_OnPanChange),
	rrentry(CSurf_OnVolumeChangeEx),
	rrentry(CSurf_OnPanChangeEx),

	rrentry(CSurf_OnMuteChange),
	rrentry(CSurf_OnSelectedChange),
	rrentry(CSurf_OnSoloChange),
	rrentry(CSurf_OnFXChange),
	rrentry(CSurf_OnRecArmChange),


	rrentry(CSurf_OnVolumeChange),
	rrentry(GetTrackGUID),
	rrentry(genGuid),
	rrentry(guidToString),
	rrentry(stringToGuid),

	rrentry(InsertTrackAtIndex),
	rrentry(DeleteTrack),
	rrentry(CSurf_OnTrackSelection),
	rrentry(SetTrackAutomationMode),
	rrentry(TrackFX_GetCount),
	rrentry(TrackFX_GetNumParams),
	rrentry(GetTrackNumMediaItems),

	rrentry(TrackFX_GetFXName),
	rrentry(TrackFX_GetParam),
	rrentry(TrackFX_SetParam),
	rrentry(TrackFX_GetParamName),

	rrentry(TrackFX_FormatParamValue),
	rrentry(GetSetMediaTrackInfo),

	{NULL, NULL}  /* double-null terminator */
};

void reaperNumberConstant(lua_State *L, const char *name, double val)
{
	luaL_checktype(L, -1, LUA_TTABLE);

	lua_pushstring(L, name);
    lua_pushnumber(L, val);
    lua_settable(L, -3);
}

void reaperBoolConstant(lua_State *L, const char *name, bool val)
{
	luaL_checktype(L, -1, LUA_TTABLE);

	lua_pushstring(L, name);
    lua_pushboolean(L, val);
    lua_settable(L, -3);
}

void reaperStringConstant(lua_State *L, const char *name, const char *val)
{
	luaL_checktype(L, -1, LUA_TTABLE);

	lua_pushstring(L, name);
    lua_pushstring(L, val);
    lua_settable(L, -3);
}

// makes a new child table in the current table
// leaves child table on stack so it can have stuff stuffed into it

void reaperTable(lua_State *L, const char *name)
{
	luaL_checktype(L, 1, LUA_TTABLE);	// make sure it has a parent

	lua_newtable(L);			// parent child

	lua_pushstring(L, name);	// parent child name
	lua_pushvalue(L, -2);		// parent child name child

	lua_settable(L, -4);		// parent child
}

void reaperMainTable(lua_State *L)
{
	luaL_openlib(L, "reaper", reaReg, 0);

	lua_getglobal(L, "reaper");	// get it at top of stack so we can use it easily

	reaperSubTables(L);

	reaperMetatables(L);

	reaperNumberConstant(L, "PluginVersion", REAPER_PLUGIN_VERSION);

// InsertMedia mode: 0=add to current track, 1=add new track, 3=add to selected items as takes
	reaperNumberConstant(L, "InsertCurrent", 0);
	reaperNumberConstant(L, "InsertNew", 1);
	reaperNumberConstant(L, "InsertTakes", 3);

// GetTrackInfo flags:
	reaperNumberConstant(L, "TrackFolder", 1);
	reaperNumberConstant(L, "TrackSelected", 2);
	reaperNumberConstant(L, "TrackFxEnabled", 4);
	reaperNumberConstant(L, "TrackMuted", 8);
	reaperNumberConstant(L, "TrackSoloed", 16);
	reaperNumberConstant(L, "TrackSIP", 32);
	reaperNumberConstant(L, "TrackRecArmed", 64);

// time formatting mode overrides:
	reaperNumberConstant(L, "TimeFormatDefault", -1);
	reaperNumberConstant(L, "TimeFormatTime", 0);
	reaperNumberConstant(L, "TimeFormatMeasBeatTime", 1);
	reaperNumberConstant(L, "TimeFormatMeasBeat", 2);
	reaperNumberConstant(L, "TimeFormatSeconds", 3);
	reaperNumberConstant(L, "TimeFormatSamples", 4);
	reaperNumberConstant(L, "TimeFormatHMSF", 5);

// GetSetRepeat setting

	reaperNumberConstant(L, "RepeatQuery", -1);
	reaperNumberConstant(L, "RepeatClear", 0);
	reaperNumberConstant(L, "RepeatSet", 1);
	reaperNumberConstant(L, "RepeatToggle", 2);

// GetMediaItemTake gets a take from an item, or -1 for the "active" take
	reaperNumberConstant(L, "ActiveTake", -1);

// Some constants for keystack()ing
	reaperNumberConstant(L, "VK_SHIFT", 16);
	reaperNumberConstant(L, "VK_CONTROL", 17);
	reaperNumberConstant(L, "VK_MENU", 18);
	reaperNumberConstant(L, "VK_ALT", 18);		// since I can never remember VK_MENU


// Very useful debug string
	reaperStringConstant(L, "NameOfGod", "Eric Clapton");

	lua_pop(L, 1);
}


void stackDump (lua_State *L) {
	int i;
	int top = lua_gettop(L);
	for (i = 1; i <= top; i++) {  /* repeat for each level */
		int t = lua_type(L, i);
		switch (t) {

		  case LUA_TSTRING:  /* strings */
			  printf("`%s'", lua_tostring(L, i));
			  break;

		  case LUA_TBOOLEAN:  /* booleans */
			  printf(lua_toboolean(L, i) ? "true" : "false");
			  break;

		  case LUA_TNUMBER:  /* numbers */
			  printf("%g", lua_tonumber(L, i));
			  break;

		  case LUA_TTABLE:	/* tables */
			  printf("table");
			  break;

		  default:  /* other values */
			  printf("%s", lua_typename(L, t));
			  break;

		}
		printf("  ");  /* put a separator */
	}
	printf("\n");  /* end the listing */
}
